package client

import (
	"bufio"
	"fmt"
	"github.com/golang/protobuf/proto"
	"go-arch/network/chatroom/constants"
	msg "go-arch/network/chatroom/pb"
	"go-arch/network/chatroom/util"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

type SimpleClient struct {
	exit           chan bool
	consoleMsgChan chan ConsoleMsg
	conn           net.Conn
	name           string
	id             int32
}

type ConsoleMsg struct {
	cmd     string
	message string
}

func NewSimpleClient(name, address string, id int32) (Client, error) {
	client := &SimpleClient{
		exit:           make(chan bool),
		consoleMsgChan: make(chan ConsoleMsg),
		name:           name,
		id:             id,
	}
	go client.startConsole()
	go client.handleConsoleMsg()
	err := client.Connect(address)
	if err != nil {
		return nil, err
	}
	<-client.exit
	return client, nil
}

func (s *SimpleClient) printInfo(info string) {
	fmt.Printf("[%s]: %s\n", s.name, info)
}

func (s *SimpleClient) Connect(address string) error {
	conn, err := net.Dial("tcp4", address)
	if err != nil {
		return err
	}
	s.conn = conn
	go s.handleReceiveMsg()
	return nil
}

func (s *SimpleClient) Name() string {
	return s.name
}

func (s *SimpleClient) Send(msg *msg.Msg) error {
	bytes, err := proto.Marshal(msg)
	if err != nil {
		return err
	}
	conn := s.conn
	sendLen, err := conn.Write(bytes)
	s.printInfo(fmt.Sprintf("send message length: %d\n", sendLen))
	if err != nil {
		return err
	}
	return nil
}

func (s *SimpleClient) Close() {
	close(s.consoleMsgChan)
	s.exit <- true
}

func (s *SimpleClient) startConsole() {
	reader := bufio.NewReader(os.Stdin)
	s.printInfo("simple shell")
	for {
		fmt.Printf("%s->", s.name)
		text, _ := reader.ReadString('\n')
		texts := strings.Split(text, ",")
		if len(texts) == 2 {
			consoleMsg := ConsoleMsg{
				cmd:     texts[0],
				message: texts[1],
			}
			s.consoleMsgChan <- consoleMsg
		}
	}
}

func (s *SimpleClient) handleConsoleMsg() {
	for {
		if consoleMsg, ok := <-s.consoleMsgChan; ok {
			message := consoleMsg.message
			cmdDesc := consoleMsg.cmd
			cmd := constants.Commands[cmdDesc]
			switch cmd {
			case constants.Login:
				m := &msg.Msg{
					Magic:     constants.Magic,
					Version:   constants.Version,
					Cmd:       constants.Login,
					MsgType:   constants.Text,
					MsgStatus: constants.Request,
					MsgId:     int64(util.RandomId()),
					FromId:    s.id,
					ToId:      0,
					Body:      []byte(message),
					Extend:    "",
					Timestamp: time.Now().Unix(),
				}

				err := s.Send(m)
				if err != nil {
					log.Printf("send message failed , err: %v\n", err)
					continue
				}
				break
			case constants.CreateRoom:
				m := &msg.Msg{
					Magic:     constants.Magic,
					Version:   constants.Version,
					Cmd:       constants.CreateRoom,
					MsgType:   constants.Text,
					MsgStatus: constants.Request,
					MsgId:     int64(util.RandomId()),
					FromId:    s.id,
					ToId:      0,
					Body:      []byte(message),
					Extend:    "",
					Timestamp: time.Now().Unix(),
				}

				err := s.Send(m)
				if err != nil {
					log.Printf("send message failed , err: %v\n", err)
					continue
				}
				break
			case constants.ListRoomMembers:
				m := &msg.Msg{
					Magic:     constants.Magic,
					Version:   constants.Version,
					Cmd:       constants.ListRoomMembers,
					MsgType:   constants.Text,
					MsgStatus: constants.Request,
					MsgId:     int64(util.RandomId()),
					FromId:    s.id,
					ToId:      0,
					Body:      []byte(message),
					Extend:    "",
					Timestamp: time.Now().Unix(),
				}

				err := s.Send(m)
				if err != nil {
					log.Printf("send message failed , err: %v\n", err)
					continue
				}
				break
			case constants.JoinRoom:
				m := &msg.Msg{
					Magic:     constants.Magic,
					Version:   constants.Version,
					Cmd:       constants.JoinRoom,
					MsgType:   constants.Text,
					MsgStatus: constants.Request,
					MsgId:     int64(util.RandomId()),
					FromId:    s.id,
					ToId:      0,
					Body:      []byte(message),
					Extend:    "",
					Timestamp: time.Now().Unix(),
				}

				err := s.Send(m)
				if err != nil {
					log.Printf("send message failed , err: %v\n", err)
					continue
				}
				break
			default:
				fmt.Printf("not support this type[%s] of cmd\n", cmdDesc)
				break

			}
		}
	}
}

func (s *SimpleClient) handleReceiveMsg() {
	for {
		conn := s.conn
		container := make([]byte, 1024)
		read, err := conn.Read(container)
		if err != nil {
			log.Printf("recv message failed , err: %v\n", err)
			continue
		}

		bytes := container[:read]
		m := &msg.Msg{}
		_ = proto.Unmarshal(bytes, m)
		s.handleMsg(m)
	}
}

func (s *SimpleClient) handleMsg(m *msg.Msg) {
	log.Println(m)
}
